package agent;

import common.Move;
import common.Piece;
import common.Side;
import java.util.LinkedList;
import java.util.List;

/**
 * Represents a fictitious state with the agent pieces and the "guessed"
 * opponent pieces (and other things). It is used by the alpha-beta algorithm
 * during the search process. Sugestions: - Define an attribute that indicates
 * which player plays in this state. - When the successors of a state are
 * generated (getSuccessors), you should determine for each successor what is
 * the player which plays in that state. This information is needed so that
 * alpha-beta algorithm can decide, for each succesor, if it calls maxValue or
 * minValue. - Override method isValidMove because, if not, all moves are
 * considered valid because the list of played pieces is not used in the search
 * process and is always empty (see the isValidMove method in class
 * DominoState). Notice that, during the search process you don't need to use
 * the already list of played pieces (defined in class DominoState), so, it is
 * always empty. You just need to know what are the left and right values in the
 * extremities of the already played pieces (also defined in class DominoState).
 * - You should not use the placePiece method as it is implemented in class
 * AgentCurrentState because it was conceived for the "real" state that the
 * agent knows (for example, it doesn't remove opponent pieces when it plays).
 * You should implement another method (maybe with the same name) that places a
 * piece and makes the necessary modifications, namely, it should remove a piece
 * from the playing player, create the Move describing the... move and determine
 * the next player to play. - You should, obviously, implement methods
 * getSuccessors, utility and evaluate and isEndOfGameState. - Other aspects you
 * may consider necessary.
 */
public class AgentSearchState extends AgentState {

    private LinkedList<Piece> opponentPieces;
    private Player player;
    public Move move;

    public AgentSearchState(LinkedList<Piece> agentPieces, LinkedList<Piece> opponentPieces, byte leftValue, byte rightValue, Player player) {
        super(agentPieces);
        this.opponentPieces = opponentPieces;
        this.player = player;
        this.rightValue = rightValue;
        this.leftValue = leftValue;
    }

//    When the successors of a state are generated (getSuccessors), you should
// *   determine for each successor what is the player which plays in that state.
// *   This information is needed so that alpha-beta algorithm can decide, for each
// *   succesor, if it calls maxValue or minValue.
    public List<AgentSearchState> getSucessors() {
        List<AgentSearchState> sucessorsList = new LinkedList<>();
        LinkedList<Piece> pointer = null;
        if (player == Player.AGENT) {
            pointer = (LinkedList) agentPieces.clone();
        } else if (player == Player.OPPONENT) {
            pointer = (LinkedList) opponentPieces.clone();
        }
        for (Piece piece : pointer) {
            AgentSearchState sucessor;
            if (isValidMove(Side.LEFT, piece)) {
                sucessor = (AgentSearchState) this.clone();
                sucessor.placePiece(player, Side.LEFT, piece);
                sucessorsList.add(sucessor);
            }
            if (isValidMove(Side.RIGHT, piece)) {
                sucessor = (AgentSearchState) this.clone();
                sucessor.placePiece(player, Side.RIGHT, piece);
                sucessorsList.add(sucessor);
            }
        }

        return sucessorsList;
    }

    @Override
    public boolean isValidMove(Side side, Piece piece) {
        return side == Side.LEFT && piece.hasValue(leftValue) || side == Side.RIGHT && piece.hasValue(rightValue);
    }

// * - You should not use the placePiece method as it is implemented in class
// * AgentCurrentState because it was conceived for the "real" state that the
// * agent knows (for example, it doesn't remove opponent pieces when it plays).
// * You should implement another method (maybe with the same name) that places a
// * piece and makes the necessary modifications, namely, it should remove a piece
// * from the playing player, create the Move describing the... move and determine
// * the next player to play.    
    public boolean placePiece(Player player, Side side, Piece piece) {
        if (side == Side.LEFT) {
            this.leftValue = piece.getOppositeValue(leftValue);
        } else {
            this.rightValue = piece.getOppositeValue(rightValue);
        }
        
        if (player == Player.AGENT) {
            this.agentPieces.remove(piece);
            this.move = new Move(side, piece);
        } else if (player == Player.OPPONENT) {
            this.opponentPieces.remove(piece);
            this.move = new Move(side, piece);
            this.player = Player.AGENT;
        }
        
        decidePlayer(player);
        
        return true;
    }
    
    public void decidePlayer(Player lastPlayer){
        Player p = lastPlayer == Player.AGENT ? Player.OPPONENT : Player.AGENT;
        LinkedList<Piece> pointer = new LinkedList<>();
        if(p == Player.AGENT)
        {
            pointer = (LinkedList<Piece>) agentPieces.clone();
        }else if(p == Player.OPPONENT)
        {
            pointer = (LinkedList<Piece>) opponentPieces.clone();
        }
        if(playerCanPlay(pointer))
        {
            this.player = p;
        }
        else
        {
            this.player = p == Player.AGENT ? Player.OPPONENT : Player.AGENT;
        }
    }

    //Maybe you need at least one more constructor...
    //construtor a receber um agentcurrentstate
    //...
    public boolean isEndOfGameState() {
        return (agentPieces.isEmpty() || opponentPieces.isEmpty())
                || ((!playerCanPlay(agentPieces) && !playerCanPlay(opponentPieces)));
    }

    public boolean playerCanPlay(LinkedList<Piece> playerList) {
        for (Piece p : playerList) {
            if (p.hasValue(leftValue) || p.hasValue(rightValue)) {
                return true;
            }
        }
        return false;
    }

    public double evaluate() {
        if (isEndOfGameState()) {
            return utility();
        }
        int calc = 0;
        calc = getAllDotsPlayer(opponentPieces);
        calc -= getAllDotsPlayer(agentPieces);
        return calc;
    }

    private double utility() {
        if (agentPieces.isEmpty()) {
            return 1000;
        }
        if (opponentPieces.isEmpty()) {
            return -1000;
        }
        return 0;

    }

    // Maybe you need to override the clone method...
    @Override
    public Object clone() {
        AgentSearchState c = new AgentSearchState(
                (LinkedList<Piece>) agentPieces.clone(), 
                (LinkedList<Piece>) opponentPieces.clone(),
                leftValue,
                rightValue, 
                player);
        return c;
    }

    public Move getMove() {
        return move;
    }

    public Player getPlayer() {
        return player;
    }

    public int getAllDotsPlayer(LinkedList<Piece> playerList) {
        int soma = 0;
        for (Piece p : playerList) {
            soma += p.getValue1() + p.getValue2();
        }
        return soma;
    }

//    public AgentSearchState skipAMove(){
//        AgentSearchState ass = (AgentSearchState) this.clone();
//        if(ass.player == Player.AGENT){
//            ass.player = Player.OPPONENT;
//        }else if(ass.player == Player.OPPONENT){
//            ass.player = Player.AGENT;
//            ass.recalcOpponentPossiblePiecesBasedOnPlayerSkip(leftValue, rightValue);
//        }
//        return ass;
//    }
}
